// Copyright 2008 Senary
// See credits.txt for details.

// This file is part of Omega VII.

// Omega VII is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Omega VII is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Omega VII.  If not, see <http://www.gnu.org/licenses/>.

#include <string>
#include <windows.h> //required by adll.h

#include "sdk_engine/adll.h"
#include "SpecialEffects.h"
#include "Vector.h"
#include "SpaceObject.h"
#include "GameManager.h"
#include "Constants.h"
#include "MediaManager.h"

using namespace std;

#define VECTOR_LENGTH(vec) sqrt(_FLOAT(vec->x)*_FLOAT(vec->x)+_FLOAT(vec->y)*_FLOAT(vec->y)+_FLOAT(vec->z)*_FLOAT(vec->z))

///////////////////////////////////////////////////////////////////////////////////////////
//	SPECIAL EFFECT - EXPLOSION
//	
//	Call by:	void explode(ENTITY* e)
//
//	Reference:	3D GAME STUDIO SPECIAL EFFECTS v1.31 (for A5 Engine 5.1)
//				Dan Niezgocki / Conitec March 2002
///////////////////////////////////////////////////////////////////////////////////////////

#define SPRITE_EXPLOSION "expl2+16.pcx"
#define PIC_EXPLOSION "expl.bmp"
#define SOUND_EXPLOSION "exp.ogg"
#define TIME 0.8

void explosionEvent(PARTICLE* p) {

	if(p->lifespan>_VAR(3)) {p->alpha+=_VAR(TIME);}
	if(p->lifespan<=_VAR(3)) {p->alpha-=_VAR(TIME);}
	if(p->alpha<=_VAR(0)) {p->lifespan=_VAR(0);}
}

void explodeParticle(PARTICLE* p) {
	static BMAP* explo_map = MediaManager::getInstance()->getImage(PIC_EXPLOSION);

	p->vel_x=random(_VAR(60))-_VAR(30);
	p->vel_y=random(_VAR(60))-_VAR(30);
	p->vel_z=random(_VAR(10))+_VAR(20);

	p->flags|=MOVE|STREAK|BRIGHT;
	p->bmap=explo_map;
	p->gravity=_VAR(6);
	p->alpha=_VAR(30);
	p->lifespan=_VAR(6);
	p->size=random(_VAR(2))+_VAR(1);
	p->event=(EVENT)explosionEvent;
}

void animateExplosion2(ENTITY* e) {
	if(e->frame<_VAR(16)) {
		e->lightrange+=_VAR(15);
		e->red+=_VAR(20*TIME);
		e->blue-=_VAR(20*TIME);
		e->frame+=_VAR(TIME);
	}
	else {
		ent_remove(e);
	}
}

void animateExplosion(ENTITY* e) {
	static SOUND* explosion = MediaManager::getInstance()->getSound(SOUND_EXPLOSION);

	e->flags|=FACING|PASSABLE|ZNEAR|BRIGHT|UNLIT|TRANSLUCENT;
	e->frame=_VAR(1);
	e->red=_VAR(210);
	e->green=_VAR(100);
	e->blue=_VAR(100);
	e->ambient=_VAR(100);
	e->lightrange=_VAR(110);
	e->scale_x=_VAR(0.5);
	e->scale_y=_VAR(0.5);
	e->scale_z=_VAR(0.5);

	e->emask|=DYNAMIC|ENABLE_FRAME;

	snd_play(explosion,_VAR(70),_VAR(0));
	e->event=(EVENT)animateExplosion2;
}

void explode(Vector const& pos) {
	VECTOR gsPos;
	pos.toGamestudio(&gsPos);

	effect((EVENT)explodeParticle, _VAR(1), &gsPos, NULL);
	ent_create(SPRITE_EXPLOSION, &gsPos,
		(EVENT)animateExplosion);
}

///////////////////////////////////////////////////////////////////////////////////////////
//	SPECIAL EFFECT - SMOKE EFFECT
//
//	Call by:		void smokeEffect(ENTITY* e)
//	To off smoke:	void offSmokeEffect(ENTITY* e)
///////////////////////////////////////////////////////////////////////////////////////////

#define PIC_SMOKE "dustplume.tga"
#define VAR_PARTICLE_SIZE _VAR(10)
#define VAR_FADE_SPEED _VAR(1)
#define VAR_SMOKE_NUMBER _VAR(1)

#define VAR_XDISP _VAR(4)
#define VAR_YDISP _VAR(4)
#define VAR_ZDISP _VAR(4)

#define ANGLE skill[80]
#define ON_OFF skill[81]

void smokeEffect(ENTITY* e) 
{
	if(e->ON_OFF==_VAR(0)) {
		e->emask |= DYNAMIC|ENABLE_FRAME;
		e->ON_OFF = _VAR(1);
		e->event=(EVENT)generateSmoke;
	}
}

void offSmokeEffect(ENTITY* e) {
	if(e->ON_OFF==_VAR(1)) {
		e->ON_OFF = _VAR(0);
		e->event=NULL;
	}
}

void smoke_effect(PARTICLE* p)
{
	static BMAP* smokebmap = MediaManager::getInstance()->getImage(PIC_SMOKE);

	p->ANGLE	= random(_VAR(360));

	p->vel_x	= fsin(p->ANGLE,random(_VAR(1)));
	p->vel_y	= fcos(p->ANGLE,random(_VAR(1)));
	p->vel_z	= random(_VAR(1))+_VAR(1);
	p->size		= random(VAR_PARTICLE_SIZE);
	p->alpha	= _VAR(100);

	p->flags	|=MOVE|BRIGHT|OVERLAY;
	p->bmap		= smokebmap;	
	p->x += random(VAR_XDISP *2) - VAR_XDISP;
	p->y += random(VAR_YDISP *2) - VAR_YDISP;
	p->z += random(VAR_ZDISP *2) - VAR_ZDISP;
	p->lifespan = _VAR(50);
	p->event	= (EVENT)smoke_fade; // fade out the particle
}

void smoke_fade(PARTICLE* p)
{
	p->ANGLE += _VAR(10);
	p->vel_x=fsin(p->ANGLE,random(_VAR(1)));
	p->vel_y=fcos(p->ANGLE,random(_VAR(1)));
	p->alpha -= VAR_FADE_SPEED;
	if(p->alpha <= _VAR(0))
	{
		p->lifespan = _VAR(0);
	}
}

void generateSmoke(ENTITY* e) 
{
	effect((EVENT)smoke_effect,VAR_SMOKE_NUMBER,::vector(e->x,e->y,e->z),NULL);
}

///////////////////////////////////////////////////////////////////////////////////////////
//	SPECIAL EFFECT - RECEIVE DMG
//
//	Call by:		void receiveDamage(ENTITY* e)
///////////////////////////////////////////////////////////////////////////////////////////

#define PIC_RECDMG "expl.bmp"

void receiveDamage(Vector const& pos) {
	VECTOR gsPos;
	pos.toGamestudio(&gsPos);

	effect((EVENT)shotParticle, _VAR(1), &gsPos, NULL);
}

void shotParticle(PARTICLE* p) {
	static BMAP* recdmg_map = MediaManager::getInstance()->getImage(PIC_RECDMG);

	p->vel_x=random(_VAR(10))-_VAR(5);
	p->vel_y=random(_VAR(10))-_VAR(5);
	p->vel_z=random(_VAR(3))+_VAR(5);

	p->flags|=MOVE|STREAK|BRIGHT;
	p->bmap=recdmg_map;
	p->gravity=_VAR(2);
	p->alpha=_VAR(30);
	p->lifespan=_VAR(6);
	p->size=random(_VAR(1));
	p->event=(EVENT)recDmgEvent;
}

void recDmgEvent(PARTICLE* p) {
	if(p->lifespan>_VAR(3)) {p->alpha+=_VAR(TIME);}
	if(p->lifespan<=_VAR(3)) {p->alpha-=_VAR(TIME);}
	if(p->alpha<=_VAR(0)) {p->lifespan=_VAR(0);}
}

///////////////////////////////////////////////////////////////////////////////////////////
//	SPECIAL EFFECTS - OPEN FIRE EFFECT
//
//	Call by:		void openFire(ENTITY* e)
//
///////////////////////////////////////////////////////////////////////////////////////////

#define PIC_OPENFIRE "shoot.bmp"
#define SOUND_FIRE "laser.ogg"

void openFire(SpaceObject const* so) {
	static SOUND* s = MediaManager::getInstance()->getSound(SOUND_FIRE);

	VECTOR gsAngle;
	VECTOR gsPos;
	// We assume that the angle is a unit vector (should be the case)
	so->getAngle().toGamestudio(&gsAngle);
	so->getPosition().toGamestudio(&gsPos);
	
	effect((EVENT)fireParticles, _VAR(1), &gsPos, &gsAngle);
	snd_play(s, _VAR(10), _VAR(0));
}

void fireParticles(PARTICLE* p) {
	static BMAP* firebmap = MediaManager::getInstance()->getImage(PIC_OPENFIRE);
	
	p->vel_x*=50;
	p->vel_y*=50;
	p->vel_z*=50;

	p->size		= _VAR(2);
	p->alpha	= _VAR(70);

	p->flags	|=MOVE|BRIGHT|BEAM|TRANSLUCENT;
	p->bmap		= firebmap;	
	p->lifespan = _VAR(50);
	p->event=(EVENT)fireFade;
}

void fireFade(PARTICLE* p) {
	p->alpha -= _VAR(1000);
	if(p->alpha <= _VAR(0))
	{
		p->lifespan = _VAR(0);
	}
}

///////////////////////////////////////////////////////////////////////////////////////////
//	SPECIAL EFFECTS - OPEN FIRE EFFECT
//
//	Call by:		void methiFire(ENTITY* e)
//
///////////////////////////////////////////////////////////////////////////////////////////

#define THETA_X e->roll
#define THETA_Y 0
#define THETA_Z e->pan

#define METHI_DISP 7.0

void methiFire(SpaceObject const* so) {
	static SOUND* s= MediaManager::getInstance()->getSound(SOUND_FIRE);

	// We assume that the pan angle of the ship is always zero.
	// This follows from the way we store angles (as vector).
	// Thus the spaceship's up direction is always towards positive z.
	Vector up(0.0, 0.0, 1.0);
	Vector right = cross(so->getAngle(), up);
	if (right.r2() < 0.001) {
		// spaceship is facing upwards... degenerate case
		right = Vector(1.0, 0.0, 0.0);
	} else {
		right /= right.r();
	}

	Vector pos1 = so->getPosition() + right * METHI_DISP;
	Vector pos2 = so->getPosition() - right * METHI_DISP;

	VECTOR gsPos1;
	VECTOR gsPos2;
	VECTOR gsAngle;

	pos1.toGamestudio(&gsPos1);
	pos2.toGamestudio(&gsPos2);
	so->getAngle().toGamestudio(&gsAngle);

	effect((EVENT)fireParticles, _VAR(1), &gsPos1, &gsAngle);
	effect((EVENT)fireParticles, _VAR(1), &gsPos2, &gsAngle);

	snd_play(s,_VAR(30),_VAR(0));
}

///////////////////////////////////////////////////////////////////////////////////////////
//	SPECIAL EFFECTS - TRAIL EFFECT
//
//	Call by:		void trail(ENTITY* e)
//
///////////////////////////////////////////////////////////////////////////////////////////

#define PIC_TRAIL "trail.bmp"
#define SOUND_TRAIL ""

void trail(SpaceObject const* so) {
	VECTOR gsAngle;
	VECTOR gsPos;

	// We assume the angle is normalized
	so->getAngle().toGamestudio(&gsAngle);
	so->getPosition().toGamestudio(&gsPos);

	effect((EVENT)trailParticles,_VAR(1),&gsPos, &gsAngle);
}

void trailParticles(PARTICLE* p) {
	static BMAP* trailbmap = MediaManager::getInstance()->getImage(PIC_TRAIL);
	p->vel_x*=-0.5f;
	p->vel_y*=-0.5f;
	p->vel_z*=-0.5f;

	p->size		= _VAR(5);
	p->alpha	= _VAR(70);

	p->flags	|=MOVE|BRIGHT|STREAK|TRANSLUCENT;
	p->bmap		= trailbmap;	
	p->lifespan = _VAR(50);
	p->event=(EVENT)trailFade;
}

void trailFade(PARTICLE* p) {
	p->alpha -= _VAR(1);
	if(p->alpha <= _VAR(0))
	{
		p->lifespan = _VAR(0);
	}
}



